package com.example.mytestdemo.view.camera3dapplication;


import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import androidx.annotation.Nullable;
import com.example.mytestdemo.R;
import java.util.ArrayList;
import java.util.List;

/**
 * 使用camera实现3d效果的自定义控件
 */
public class Camera3DView extends View {
    //存放bitmap资源文件id的集合
    private List<Integer> bitmapResourceIds;
    //用于3d变换
    private Camera camera;
    //用于变换的矩阵
    private Matrix matrix;
    //view的宽高
    private float viewWidth, viewHeight;
    //是否绘制完毕
    private boolean isDrawFinished = false;
    public static final int VERTICAL = 1;
    public static final int HORIZONTAL = 0;
    //旋转方向
    private int orientation = VERTICAL;
    //需要平移的旋转轴，所有旋转操作无外乎两个玩意，一个是旋转轴，一个是旋转角度
    //VERTICAL时使用rotateY，HORIZONTAL时使用rotateX
    private float rotatePivotX, rotatePivotY;
    //旋转角度
    private float degress;
    //最大旋转角度
    private float maxDegress = 90;
    //当前图片索引
    private int currentIndex;
    //下个显示图片索引
    private int nextIndex;
    //上个显示图片索引
    private int preIndex;
    private Paint mPaint;
    //是否前进
    private boolean isForward = true;
    private ValueAnimator valueAnimator;
    private boolean isAnimatiorRunning;
    //默认3D模式
    public static final int MODE_NORMAL = 0;
    //百叶窗模式
    public static final int MODE_BLINDS = 1;
    //滚动
    public static final int MODE_ROLLINTURN = 2;

    private int mode = MODE_NORMAL;
    //切分
    private int partNumber = 1;
    private int partWidth, partHeight;
    private int turnDegress = 15;

    public Camera3DView(Context context) {
        this(context, null);
    }

    public Camera3DView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Camera3DView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        bitmapResourceIds = new ArrayList<>();
        camera = new Camera();
        matrix = new Matrix();
        mPaint = new Paint();

        isDrawFinished = false;

        TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.Camera3DView);
        setMode(typedArray.getInt(R.styleable.Camera3DView_rotate_mode, MODE_NORMAL));
        setOrientation(typedArray.getInt(R.styleable.Camera3DView_rotate_orientation, VERTICAL));
        setPartNumber(typedArray.getInt(R.styleable.Camera3DView_partNumber, 1));
        //资源释放
        typedArray.recycle();
    }

    public void setMode(int mode) {
        this.mode = mode;
        if (mode == MODE_BLINDS) {//百叶窗需要旋转180度
            maxDegress = 180;
        } else {
            maxDegress = 90;
        }
    }

    public void setPartNumber(int partNumber) {
        this.partNumber = partNumber;
        calcPartDimension();
    }

    //计算切分的宽高
    private void calcPartDimension() {
        partWidth = (int) (viewWidth / (float) partNumber);
        partHeight = (int) (viewWidth / (float) partNumber);
    }

    /**
     * 添加图片
     *
     * @param resId
     */
    public void addResId(int resId) {
        bitmapResourceIds.add(resId);
        if (isDrawFinished) {
            invalidate();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
        viewHeight = h;
        isDrawFinished = true;
        initBitmaps();
        resetIndex();
        calcPartDimension();
    }

    private void initBitmaps() {
        for (int i = 0; i < bitmapResourceIds.size(); i++) {
            int resID = bitmapResourceIds.get(i);
            getBitmapScale(resID, viewWidth, viewHeight);
        }
    }

    /**
     * 旋转一面后，调用重置索引
     */
    public void resetIndex() {
        int listSize = bitmapResourceIds.size();
        if (isForward) {//前进或者向下
            currentIndex++;
            if (currentIndex > listSize - 1) {
                currentIndex = 0;
            }
        } else {
            currentIndex--;
            if (currentIndex < 0)
                currentIndex = listSize - 1;
        }

        nextIndex = currentIndex + 1;
        preIndex = currentIndex - 1;
        if (nextIndex > listSize - 1)
            nextIndex = 0;//循环切换
        if (preIndex < 0)
            preIndex = listSize - 1;

        this.degress = 0;

        rotatePivotX = 0;
        rotatePivotY = 0;

        isForward = true;

        invalidate();
    }

    public void setDegress(int degress) {
        this.degress = degress;

        //VERTICAL时使用rotateY，HORIZONTAL时使用rotateX
        if (orientation == HORIZONTAL) {
            //x方向旋转轴随degress的变大不断下移
            rotatePivotX = degress / maxDegress * viewWidth;
        } else {
            //y方向旋转轴随degress的变大不断右移
            rotatePivotY = degress / maxDegress * viewHeight;
        }

        //刷新
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        switch (mode) {
            case MODE_NORMAL:
                drawModeNormal(canvas);
                break;
            case MODE_BLINDS:
                drawModeBlinds(canvas);
                break;
            case MODE_ROLLINTURN:
                drawModeRollInTurn(canvas);
                break;
        }

    }

    /**
     * 滚动效果
     *
     * @param canvas
     */
    private void drawModeRollInTurn(Canvas canvas) {
        //VERTICAL时使用rotateY，HORIZONTAL时使用rotateX
        if (orientation == VERTICAL) {
            for (int i = 0; i < partNumber; i++) {
                //之前增大旋转的最大角度，所以为了让每个块启动动画都不同，减去对应每个块的turnDegress角度
                float degressTemp = degress - i * turnDegress;
                if (degressTemp < 0) {//为负数的情况不动
                    degressTemp = 0;
                }
                if (degressTemp > maxDegress) {//转完了也不动
                    degressTemp = maxDegress;
                }

                //重新计算下旋转轴偏移
                float tAxisY = degressTemp / maxDegress * viewHeight;
                if (tAxisY > viewHeight)
                    tAxisY = viewHeight;
                if (tAxisY < 0)
                    tAxisY = 0;

                //如果是前进，则画当前图，后退则画上一张图,注释用的是前进情况
                matrix.reset();
                camera.save();
                //旋转角度 0 - -maxDegress 
                camera.rotateX(-degressTemp);
                camera.getMatrix(matrix);
                camera.restore();

                //绕着图片top旋转
                matrix.preTranslate(-partWidth / 2f, 0);
                //旋转轴向下平移，则图片也向下平移
                matrix.postTranslate(partWidth / 2f, tAxisY);
                matrix.postTranslate(partWidth * i, 0);
                //如果是前进，则画当前图，后退则画上一张图，因为后退时，这里画的是动画下方出来的图片，而下方的图片是前一张图
                canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? currentIndex : preIndex), partWidth * i, 0, partWidth, (int) viewHeight),
                        matrix, mPaint);

                //在处理下一张图片
                matrix.reset();
                camera.save();
                //旋转角度 maxDegress - 0
                camera.rotateX(maxDegress - degressTemp);
                camera.getMatrix(matrix);
                camera.restore();

                //绕着图片bottom旋转
                matrix.preTranslate(-partWidth / 2f, -viewHeight);
                //旋转轴向下平移，则图片也向下平移
                matrix.postTranslate(partWidth / 2f, tAxisY);
                matrix.postTranslate(partWidth * i, 0);
                //如果是前进，则画下一张图，后退则画当前图，后退时，这边代码画的是动画上方的图片，上方的图片是当前图片
                canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), partWidth * i, 0, partWidth, (int) viewHeight),
                        matrix, mPaint);
            }
        } else {
            for (int i = 0; i < partNumber; i++) {
                //之前增大旋转的最大角度，所以为了让每个块启动动画都不同，减去对应每个块的turnDegress角度
                float degressTemp = degress - i * turnDegress;
                if (degressTemp < 0) {//为负数的情况不动
                    degressTemp = 0;
                }
                if (degressTemp > maxDegress) {//转完了也不动
                    degressTemp = maxDegress;
                }

                //重新计算下旋转轴偏移
                float tAxisX = degressTemp / maxDegress * viewWidth;
                if (tAxisX > viewWidth)
                    tAxisX = viewWidth;
                if (tAxisX < 0)
                    tAxisX = 0;

                //如果是前进，则画当前图，后退则画上一张图,注释用的是前进情况
                matrix.reset();
                camera.save();
                //旋转角度 0 - maxDegress 
                camera.rotateY(degressTemp);
                camera.getMatrix(matrix);
                camera.restore();

                //绕着图片left旋转
                matrix.preTranslate(0, -partHeight / 2);
                //旋转轴向右平移，则图片也向右平移
                matrix.postTranslate(tAxisX, partHeight / 2);
                matrix.postTranslate(0, partHeight * i);
                //如果是前进，则画当前图，后退则画上一张图，因为后退时，这里画的是动画右方出来的图片，而右方的图片是前一张图
                canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? currentIndex : preIndex), 0, partHeight * i, (int) viewWidth, partHeight),
                        matrix, mPaint);

                //在处理下一张图片
                matrix.reset();
                camera.save();
                //旋转角度 -maxDegress - 0
                camera.rotateY(-maxDegress + degressTemp);
                camera.getMatrix(matrix);
                camera.restore();

                //绕着图片right旋转
                matrix.preTranslate(-viewWidth, -partHeight / 2f);
                //旋转轴向右平移，则图片也向右平移
                matrix.postTranslate(tAxisX, partHeight / 2f);
                matrix.postTranslate(0, partHeight * i);
                //如果是前进，则画下一张图，后退则画当前图，后退时，这边代码画的是动画左方的图片，左方的图片是当前图片
                canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), 0, partHeight * i, (int) viewWidth, partHeight),
                        matrix, mPaint);
            }
        }
    }

    /**
     * 百叶窗效果
     *
     * @param canvas
     */
    private void drawModeBlinds(Canvas canvas) {
        //VERTICAL时使用rotateY，HORIZONTAL时使用rotateX
        if (orientation == VERTICAL) {
            for (int i = 0; i < partNumber; i++) {
                if (degress < maxDegress / 2) {//过了一半，就优先显示下一张图，所以只在0-90度时画当前的图
                    //isForward为true时，画正面
                    matrix.reset();
                    camera.save();
                    camera.rotateX(-degress);
                    camera.getMatrix(matrix);
                    camera.restore();

                    matrix.preTranslate(-viewWidth / 2f, -partHeight / 2);
                    matrix.postTranslate(viewWidth / 2f, partHeight / 2);
                    matrix.postTranslate(0, partHeight * i);
                    canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? currentIndex : preIndex), 0, (int) (i * partHeight)
                            , (int) viewWidth, (int) partHeight), matrix, mPaint);
                } else {
                    //isForward为true时,反面
                    matrix.reset();
                    camera.save();
                    camera.rotateX(maxDegress - degress);
                    camera.getMatrix(matrix);
                    camera.restore();

                    matrix.preTranslate(-viewWidth / 2f, -partHeight / 2);
                    matrix.postTranslate(viewWidth / 2f, partHeight / 2);
                    matrix.postTranslate(0, partHeight * i);
                    canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), 0, (int) (i * partHeight)
                            , (int) viewWidth, (int) partHeight), matrix, mPaint);
                }
            }
        } else {
            for (int i = 0; i < partNumber; i++) {
                if (degress < maxDegress / 2) {//过了一半，就优先显示下一张图，所以只在0-90度时画当前的图
                    //isForward为true时，画正面
                    matrix.reset();
                    camera.save();
                    camera.rotateY(degress);
                    camera.getMatrix(matrix);
                    camera.restore();

                    matrix.preTranslate(-partWidth / 2f, -viewHeight / 2f);
                    matrix.postTranslate(partWidth / 2f, viewHeight / 2f);
                    matrix.postTranslate(partWidth * i, 0);
                    canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? currentIndex : preIndex), (int) (i * partWidth), 0
                            , (int) partWidth, (int) viewHeight), matrix, mPaint);
                } else {
                    //isForward为true时,反面
                    matrix.reset();
                    camera.save();
                    camera.rotateY(-maxDegress + degress);
                    camera.getMatrix(matrix);
                    camera.restore();

                    matrix.preTranslate(-partWidth / 2f, -viewHeight / 2f);
                    matrix.postTranslate(partWidth / 2f, viewHeight / 2f);
                    matrix.postTranslate(partWidth * i, 0);
                    canvas.drawBitmap(getPartBitmap(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), (int) (i * partWidth), 0
                            , (int) partWidth, (int) viewHeight), matrix, mPaint);
                }
            }
        }
    }

    /**
     * 3d旋转效果
     *
     * @param canvas
     */
    private void drawModeNormal(Canvas canvas) {
        //VERTICAL时使用rotateY，HORIZONTAL时使用rotateX
        if (orientation == VERTICAL) {
            //如果是前进，则画当前图，后退则画上一张图,注释用的是前进情况
            matrix.reset();
            camera.save();
            //旋转角度 0 - -maxDegress
            Log.d("tgw", "drawModeNormal: degress"+degress);
            camera.rotateX(-degress);
            camera.getMatrix(matrix);
            camera.restore();

            //https://blog.51cto.com/u_9420214/6335966
            // https://cloud.tencent.com/developer/article/1733751
            //preTranslate是指在setScale前,平移,postTranslate是指在setScale后平移
            //注意他们参数是平移的距离,而不是平移目的地的坐标!
            //由于缩放是以(0,0)为中心的,所以为了把界面的中心与(0,0)对齐,就要preTranslate(-centerX,-centerY),
            //setScale完成后,调用postTranslate(centerX,centerY),再把图片移回来,这样看到的动画效果就是activity的界面图片从中心不停的缩放了

//            这两行代码就是起到了这样的作用。
//            preTranslate方法的作用是在旋转之间先把图片向上移动图片高度的一半的距离，这样图片就关于x轴对称了，然后再进行旋转的变换
//            ，postTranslate方法是在变换之后再将图片向下移动图片高度的一半的距离也即是回到了原来的位置，这样图片显示出来的结果就是对称的了。
//            原理也很简单，旋转中心还是(0,0)，只不过我们移动图片，这样进行旋转变换的时候就会得到对称的结果了
            //
            //注:centerX和centerY是界面中心的坐标
            //绕着图片top旋转
            matrix.preTranslate(-viewWidth/2, 0f); //图片向左平移一半，
////            Log.d("tgw", "drawModeNormal:rotatePivotY---- "+rotatePivotY);
////            //旋转轴向下平移，则图片也向下平移
            matrix.postTranslate(viewWidth/2, rotatePivotY); //结束后平移回来
//            //如果是前进，则画当前图，后退则画上一张图，因为后退时，这里画的是动画下方出来的图片，而下方的图片是前一张图
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
                    matrix, mPaint);
//
            //在处理下一张图片
            matrix.reset();
            camera.save();
            //旋转角度 maxDegress - 0
            camera.rotateX(maxDegress-degress);
            camera.getMatrix(matrix);
            camera.restore();
//
//            //绕着图片bottom旋转
            matrix.preTranslate(-viewWidth / 2f, -viewHeight);
//            //旋转轴向下平移，则图片也向下平移
            matrix.postTranslate(viewWidth / 2f, rotatePivotY);
//            //如果是前进，则画下一张图，后退则画当前图，后退时，这边代码画的是动画上方的图片，上方的图片是当前图片
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
                    matrix, mPaint);
        } else {
            //如果是前进，则画当前图，后退则画上一张图,注释用的是前进情况
            matrix.reset();
            camera.save();
            //旋转角度 0 - maxDegress 
            camera.rotateY(degress);
            camera.getMatrix(matrix);
            camera.restore();

            //绕着图片left旋转
            matrix.preTranslate(0, -viewHeight / 2);
            //旋转轴向右平移，则图片也向右平移
            matrix.postTranslate(rotatePivotX, viewHeight / 2);
            //如果是前进，则画当前图，后退则画上一张图，因为后退时，这里画的是动画右方出来的图片，而右方的图片是前一张图
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? currentIndex : preIndex), viewWidth, viewHeight),
                    matrix, mPaint);

            //在处理下一张图片
            matrix.reset();
            camera.save();
            //旋转角度 -maxDegress - 0
            camera.rotateY(-maxDegress + degress);
            camera.getMatrix(matrix);
            camera.restore();

            //绕着图片right旋转
            matrix.preTranslate(-viewWidth, -viewHeight / 2f);
            //旋转轴向右平移，则图片也向右平移
            matrix.postTranslate(rotatePivotX, viewHeight / 2f);
            //如果是前进，则画下一张图，后退则画当前图，后退时，这边代码画的是动画左方的图片，左方的图片是当前图片
            canvas.drawBitmap(getBitmapScale(bitmapResourceIds.get(isForward ? nextIndex : currentIndex), viewWidth, viewHeight),
                    matrix, mPaint);
        }
    }


    /**
     * 获取缩放图片
     *
     * @param resId
     * @param width
     * @param height
     * @return
     */
    private Bitmap getBitmapScale(int resId, float width, float height) {
        if (ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId)) != null) {
            return ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId));
        }
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
        Bitmap bitmapDst = null;
//        if (ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(R.drawable.ce)) == ImageCache.getInstance().getBitmapFromMemCache(String.valueOf(resId))){
//             bitmapDst = Bitmap.createScaledBitmap(bitmap, (int) (height * 0.1), (int) height, false);
//            bitmap.recycle();
//        }else {
             bitmapDst = Bitmap.createScaledBitmap(bitmap, (int) width, (int) height, false);
            bitmap.recycle();
//        }

        ImageCache.getInstance().addBitmapToMemoryCache(String.valueOf(resId)
                , bitmapDst);
        return bitmapDst;
    }

    private Bitmap getPartBitmap(int resId, int left, int top, int width, int height) {
        StringBuffer key = new StringBuffer();
        key.append(resId).append(left).append(top).append(mode).append(orientation);
        if (ImageCache.getInstance().getBitmapFromMemCache(key.toString()) != null) {
            return ImageCache.getInstance().getBitmapFromMemCache(key.toString());
        }
        Bitmap src = getBitmapScale(resId, viewWidth, viewHeight);
        Bitmap bitmapDst = Bitmap.createBitmap(src, left, top, width, height);

        ImageCache.getInstance().addBitmapToMemoryCache(key.toString(), bitmapDst);
        return bitmapDst;
    }

    /**
     * 下一面
     */
    public void next() {
        createAnimator();

        if (!isAnimatiorRunning) {
            isForward = true;
            isAnimatiorRunning = true;
            valueAnimator.start();
        }
    }

    /**
     * 上一面
     */
    public void pre() {
        createAnimator();

        if (!isAnimatiorRunning) {
            isForward = false;
            isAnimatiorRunning = true;
            valueAnimator.reverse();
        }
    }

    private void createAnimator() {
        if (valueAnimator == null) {
            valueAnimator = ValueAnimator.ofFloat(0, 1f);
            valueAnimator.setDuration(1000);
            valueAnimator.setInterpolator(new LinearInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    degress = maxDegress * animation.getAnimatedFraction();
                    if (mode == MODE_ROLLINTURN) {
                        //滚动模式因为每个块的开始时间都有区别，我们先增大旋转的最大角度
                        degress = (maxDegress + (partNumber - 1) * turnDegress) * animation.getAnimatedFraction();
//                        degress = maxDegress ;
                    }
                    setDegress((int) degress);
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    resetIndex();
                    isAnimatiorRunning = false;
                }
            });
        }
    }

    public void setOrientation(int orientation) {
        this.orientation = orientation;
    }
}
